Manifest文件,简单的看就是当前VersionEdit的持久化信息,其中包含了:
- Comparator:全局的比较方法
- LogNumber:下一个MemTable的WAL日志文件的FileNumber
- PreviousLogNumber:已经被废弃,之前版本有用到
- NextFileNumber:下一个File的数字
- LastSequence:最新的Seq
- Compact_Pointer:在Compact一章中统一讲。
- Deteted_Files:相对于上一个Version,删除的文件
- New_Files:相对于上一个Version,新增的文件
以上内容都在VersionEditTag
中进行读写。
其中Manifest文件只会存在一个,但是名字中的FileNumber不是一定的,在数据目录下,文件名可能是
MANIFEST-000540
。个人猜想可能是版本问题导致的。
初始化:
在DbIMPL进行初始化时,会创建VersionSet,在VersionSet的构造函数中,调用了initializeIfNeeded对Manifest进行了初始化。
1 | private void initializeIfNeeded() { |
- 找出名为”CURRENT”文件,前面提到这个文件的内容就是Manifest文件的文件名。
- 这里CURRENT文件不存在,所以new一个VersionEdit,其中prevLogNumber是0,nextFileNumber是2,lastSeq也是0。
- 这里的manifestFileNumber文件是1,这里知道为什么nextFileNumber默认是2开始了吧,因为1是ManifestFile的初始化FileNumber
- 这个方法里,new了一个VersionEdit,基本上什么值也没设,这里不知道为什么要先把这个VersionEdit放进去。
- 把上面的VersionEdit写入到Manifest中
- 这里把上面的Manifest文件名,写入CURRENT文件,这个方法中用到了Temp文件。
每次Compact过后,就会调用VersionSet的logAndApply方法,把Edit的信息传入,加入到Manifest文件中。
这个方法下面会详细描述。
那么一个疑问就来了,每次Compact后都会往里面Append新的VersionEdit信息,那么这个文件不就会越来越大吗?就像Redis的Compact一样?是不是有什么机制,会导致创建新的Manifest文件,把当前的Version的快照放进去,然后丢弃旧的Manifest文件呢?
答案是有的:其中每次启动后,触发的第一次Compact,会导致旧的Manifest文件被丢弃,生成新的Manifest文件,把当前Version的快照信息放入。
这里其实有个问题的,只有每次重新启动后才会触发,如果一直在运行的话,其实不会触发重新清理的。
虽然在运行中并不会去读取这个Manifest文件,但是下次启动恢复Version信息时,需要从头到尾遍历这个文件,速度可能会很慢。
代码如下:
1 | public void logAndApply(VersionEdit edit) { |
- descriptorLog除了在这个方法,没有在其他地方被赋值过,所以第一次进来,肯定是null的
- 创建一个新的Manifest文件
- 调用writeSnapshot方法,生成VersionEdit,把当前所有的文件写入Manifest文件
- 设置CURRENT文件,指向新的Manifest文件
VersionSet的恢复:
前面提到过一个公式:OldVersion + VersionEdit = NewVersion
如果重新启动应用,要恢复到最新的Version,只要把Manifest文件中的VersionEdit全部apply一遍就行了。
方法在VersionSet::recover
中,代码浅显易懂,这里就不展开了。